const {
  parse,
  compileScript,
  rewriteDefault,
  compileTemplate,
  compileStyleAsync,
} = require('vue/compiler-sfc');
const fs = require('fs');
const path = require('path');
const hash = require('hash-sum');
const descriptorCache = new Map();

function vue() {
  let root;

  return {
    name: 'vue',
    config(config) {
      root = config.root;
      return {
        define: {
          __VUE_OPTIONS_API__: true,
          __VUE_PROD_DEVTOOLS__: false,
          _VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
        },
      };
    },
    async load(id) {
      const res = parseVueRequest(id); // id: "D:/project/vite/vite-use/src/App.vue?vue&type=style&index=0&lang=css"
      const { filename, query } = res;
      if (query.has('vue')) {
        const descriptor = await getDescriptor(filename, root);
        if (query.get('type') === 'style') {
          let block = descriptor.styles[Number(query.get('index'))];
          if (block) {
            return { code: block.content }; // 返回style的内容，transform 会处理
          }
        }
      }
    },
    async transform(code, id) {
      // code: App.vue 代码字符串, id: "D:/project/vite/vite-use/src/App.vue"
      const { filename, query } = parseVueRequest(id);
      if (filename.endsWith('.vue')) {
        if (query.get('type') === 'style') {
          const descriptor = await getDescriptor(filename, root);
          let result = await transformStyle(
            code,
            descriptor,
            query.get('index')
          );
          return result;
        } else {
          let result = await transformMain(code, filename, root);
          return result;
        }
      }
      return null;
    },
  };
}

async function transformStyle(code, descriptor, index) {
  const block = descriptor.styles[index];
  //如果是CSS，其实翻译之后和翻译之前内容是一样的
  const result = await compileStyleAsync({
    filename: descriptor.filename,
    source: code,
    id: `data-v-${descriptor.id}`, //必须传递，不然报错
    scoped: block.scoped,
  });
  let styleCode = result.code;
  const injectCode =
    `\nvar  style = document.createElement('style');` +
    `\nstyle.innerHTML = ${JSON.stringify(styleCode)};` +
    `\ndocument.head.appendChild(style);`;
  return {
    code: injectCode,
  };
}

async function getDescriptor(filename, root) {
  let descriptor = descriptorCache.get(filename);
  if (descriptor) return descriptor;
  const content = await fs.promises.readFile(filename, 'utf8'); // 读取 .vue 文件内容
  const result = parse(content, { filename }); // vue 模版解析 { template: xxx, script: xxx, styles: [xxx] }
  descriptor = result.descriptor;
  descriptor.id = hash(path.relative(root, filename));
  descriptorCache.set(filename, descriptor);
  return descriptor;
}
async function transformMain(source, filename, root) {
  const descriptor = await getDescriptor(filename, root);
  const scriptCode = genScriptCode(descriptor, filename);
  const templateCode = genTemplateCode(descriptor, filename);
  const stylesCode = genStyleCode(descriptor, filename);
  let resolvedCode = [
    stylesCode,
    templateCode,
    scriptCode,
    `_sfc_main['render'] = render`,
    `export default _sfc_main`,
  ].join('\n');
  return { code: resolvedCode };
}

function genStyleCode(descriptor, filename) {
  let styleCode = '';
  if (descriptor.styles.length) {
    descriptor.styles.forEach((style, index) => {
      const query = `?vue&type=style&index=${index}&lang=css`;
      const styleRequest = (filename + query).replace(/\\/g, '/');
      styleCode += `\nimport ${JSON.stringify(styleRequest)}`;
    });
    return styleCode;
  }
}

function genScriptCode(descriptor, id) {
  let scriptCode = '';
  let script = compileScript(descriptor, { id }); // 获取 script 里的内容
  if (!script.lang) {
    scriptCode = rewriteDefault(script.content, '_sfc_main');
  }
  return scriptCode;
}
function genTemplateCode(descriptor, id) {
  let content = descriptor.template.content; // 获取 template 里的内容
  const result = compileTemplate({ source: content, id });
  return result.code;
}
function parseVueRequest(id) {
  const [filename, querystring = ''] = id.split('?');
  let query = new URLSearchParams(querystring);
  return {
    filename,
    query,
  };
}

function parseVueRequest(id) {
  const [filename, querystring = ''] = id.split('?');
  let query = new URLSearchParams(querystring);
  return {
    filename,
    query,
  };
}

module.exports = vue;
